package FactoryType.src;

/**
 * @author SUN YE
 * @Title: test
 * @Package PACKAGE_NAME
 * @Description: 工厂模式，用于创建对象，将对象的创建延迟到子类进行实现
 * 对于复杂的对象使用工厂模式可以屏蔽底层细节提供给用户接口层面的api，但无疑会增加系统复杂度
 * 多写一个工厂对于系统来说可能并不是一个好事(要额外考虑很多东西，比如说对象具体怎么创建，创建完之后是否需要建立缓存....)
 * @date 2022-07-31
 */
public class ShapeFactory {
    //工厂类，用于生产具体对象的
    <T extends Shape> T getShapeByClass(Class<T> clazz) throws InstantiationException, IllegalAccessException {
        /* 此处生成对象的方法与工厂模式本身关联不大，此处通过反射生成对象
         * 可以灵活选取合适的对象生成方式，或者也可以使用原型模式进行生成
         * 实际中此处可能逻辑十分复杂，可以进一步使用策略模式进行解耦
         */
        T t = clazz.newInstance();
        //根据迪米特法则，其实调用者应该是不知道子类的存在的，只知道接口的存在，但此处泛型限制了返回值是接口的继承者(实现类
        //所以此处直接返回了具体的子类，当然调用者是不需要知道这些的，可以直接使用接口接受返回值
        return t;
    }
}

interface Shape {
    void draw();
}

class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Rectangle::draw() method.");
    }
}

class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Square::draw() method.");
    }
}
class Client extends ShapeFactory {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        //客户端使用工厂创建子类
        callFactory();
    }

    private static void callFactory() throws InstantiationException, IllegalAccessException {
        //工厂类通常情况下只需要一个，此处可以使用单例模式进行限制，或者直接使用spring容器中进行单例
        ShapeFactory shapeFactory = new ShapeFactory();
        //通过工厂生成具体的类，根据实际情况可以使用子类或者接口进行接收(按照迪米特法则应当使用接口接受返回值)
        Shape rectangle = shapeFactory.getShapeByClass(Rectangle.class);
        rectangle.draw();
        Shape square = shapeFactory.getShapeByClass(Square.class);
        square.draw();
    }
}